feat(wfctl): smart CI generation — cigen analyze→plan→render + ci plan/generate + MCP (PR3/7)#800
Merged
Conversation
Introduces cigen package with platform-neutral CIPlan struct and Analyze() function that derives PluginInstall, PlanGuard, Migrations, Build, Secrets, Smoke, Phases, and Triggers from typed WorkflowConfig fields. Includes golden fixture + 4 passing tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ed/case)
Extends Analyze() to union secrets.entries, ${VAR} refs from
env_vars_secret and iac.provider config fields, and migrations DBEnv
into a deduplicated SecretRef slice. Smoke derives URL+path from an
infra.container_service module's health_check + PRIMARY domain. Warnings
surface state-derived secrets (IaC output / migrations DBEnv) and
non-upper-case secret names. Multisite-shaped fixture + 4 tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RenderGitHubActions() emits a single workflow file with plan
(PR-only), apply (or apply-prereq→apply-deploy with needs for
multi-phase), optional migrations step in the last apply job,
smoke curl job, wfctl plugin install step, setup-wfctl version pin,
and per-secret ${{ secrets.NAME }} env entries. All output parses
as valid YAML. 6 tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
RenderGitLabCI() emits .gitlab-ci.yml with plan/apply/smoke stages, merge-request rules, needs-chaining for multi-phase, secret refs as $NAME CI variables, and plugin install in before_script. No deprecated 'only:' syntax. Output parses as valid YAML. 5 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the ghaInfraTemplate/ghaBuildTemplate/gitlabCITemplate text templates and their generateGitHubActions/generateGitLabCI functions with thin wrappers that build a minimal CIPlan from ciOptions and delegate to cigen.RenderGitHubActions / cigen.RenderGitLabCI. runCIGenerate now routes through cigen.Analyze + renderer with full --write guard, --diff/--exit-code, --from-plan, and --phase-config flags. Legacy ciOptions + generateCIFiles retained for backward compat. Updated ci_test.go assertions to match cigen output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds 'wfctl ci plan' subcommand (ci_plan.go) that calls cigen.Analyze and writes CIPlan JSON to --out (default stdout). Supports --phase-config for 2-phase plans, --wfctl-version pin, --branch and --runner overrides. Tests: runCIPlan writes parseable JSON with warnings+phases; runCIGenerate --from-plan loads plan and renders without re-analyzing; --diff exits 0 when files match; --write guard rejects overwrite without flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds new ci_plan MCP tool (handleCIPlan) that writes yaml_content to a temp file, calls cigen.Analyze, and returns the CIPlan JSON. Routes handleGenerateGithubActions through cigen.Analyze + RenderGitHubActions; legacy cd_yaml path retained for backward compat via mcpGenerateCDWorkflow; ci_yaml now contains cigen output. mcpAnalyzeFromYAML helper handles temp-file lifecycle for MCP yaml_content inputs. Updated 3 existing GHA tests + added 3 new ci_plan tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- resolveOutputPath: respect explicit --out dir for all paths including .github/workflows/ subdirectories (was only used for flat names) - ci_plan: add -c shorthand for --config - render_gha.go: replace b.WriteString(fmt.Sprintf()) with fmt.Fprintf - render_gitlab.go: same + use %q for quoted YAML strings - ci.go: remove unused detectModuleTypes + renderCITemplate; drop bytes/ text/template/yaml.v3 imports - mcp/wfctl_tools.go: remove unused mcpGenerateCIWorkflow golangci-lint: 0 issues Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add --help/-h/help cases to runCI switch so 'wfctl ci --help' returns nil (exit 0) instead of the error from ciUsage(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ivize trigger paths; minors
CRITICAL: MCP ci_plan wrote yaml_content to an os.CreateTemp file and
returned a CIPlan whose DeployPhase.ConfigPath pointed at the deleted
/tmp path, so generated CI steps + paths: filter referenced dead paths.
Added Options.ConfigPathAlias; mcpAnalyzeFromYAML now passes a stable
logical name (deploy.yaml / deploy.prereq.yaml).
IMPORTANT: GHA PlanGuard was a no-op (`grep -q ... || true`). Replaced
with a real gate mirroring the multisite infra.yml pattern: visible
`wfctl infra plan | tee`, then `exit 1` when the plan includes
replace/destroy. No `|| true`.
IMPORTANT: writePhasePaths leaked absolute config paths into the GHA
paths: trigger filter (never matches a checkout). Analyze now
relativizes ConfigPath to cwd via filepath.Rel, falling back to
filepath.Base when it escapes cwd.
MINORS: removed redundant `|| m.Type == "iac.provider"`; dropped the
no-op GitLab `variables: {NAME: $NAME}` block (project CI vars are
auto-injected); added explicit parens to the apply if:; added
cigen/testdata/prereq.yaml fixture.
Tests: plan-guard-real-gate, no-plan-guard-when-unset, relative-paths
filter, absolute-path-relativized, alias-verbatim, MCP
config_path-is-logical-not-temp + two-phase logical paths, GitLab
no-redundant-secret-vars.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
⏱ Benchmark Results✅ No significant performance regressions detected. benchstat comparison (baseline → PR)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
PR3 of the wfctl secrets wizard + smart CI generation cascade (design+plan in
workspace:docs/plans/2026-05-30-wfctl-secrets-wizard-and-smart-ci{-design,}.md, adversarial PASS, scope-locked). Replaces template-stamped CI generation with a config-derivedanalyze → CIPlan → renderengine. Independent of PR2; depends only on PR1 (merged).Tasks (11–16)
cigenpackage (new, exported):Analyze(configs, opts) (*CIPlan, error)derives a platform-neutral plan from the realconfig.WorkflowConfig—PluginInstall(plugin/infra module or.wfctl-lock.yaml),PlanGuard(Protectedmodules),Migrations(ci.migrations),Build.Docker(Dockerfile), deploy phases (--phase-config), triggers.secrets.entries∪${VAR}inenv_vars_secret∪ iac token/spaces ∪ migrations DB env (deduped); smoke fromhealth_check.http_path+ PRIMARY domain;Warnings[]for state-derived secret names (e.g. hash-suffixed DB URL — unknowable pre-apply) and case/alias mismatches (non-UPPER_SNAKE). Raw-map navigation is panic-safe on missing/wrong-type keys.RenderGitHubActions(plan / apply[-prereq→-deploy w/needs] / migrations / smoke jobs,${{ secrets.NAME }}refs, real plan-guard that fails on replace/destroy, relativepaths:filters) andRenderGitLabCI. Output is valid YAML (parse-back tested).wfctl ci plan(emitsCIPlanJSON for AI-stepping) + smartwfctl ci generate(--from-plan/--diff/--exit-code/--phase-config; no silent overwrite). Template consts/funcs removed.ci_plantool +generate_github_actionsrouted throughcigen.scaffold_cileft untouched (it generates aci:section from a description — the inverse of cigen's role).Review findings fixed (adversarial + code review)
ci_planno longer embeds a deleted temp path —Options.ConfigPathAliasgives a stable logical config name.|| true);paths:filters are checkout-relative (was leaking absolute paths → triggers never fired).Verification
GOWORK=off go test ./cigen/ ./mcp/ ./cmd/wfctl/→ allok;golangci-lint→0 issues.ci planconfig_path is logical (deploy.yaml, not/tmp/...); generated GHA parses as YAML; plan-guard has no|| true;paths:relative.Notes
🤖 Generated with Claude Code